<?php

namespace Framework\Foundation\Request;

use GuzzleHttp\Psr7\ServerRequest;
use Psr\Http\Message\RequestInterface;

/**
 * @method static array normalizeFiles(array $files)
 * @method static \GuzzleHttp\Psr7\UploadedFile createUploadedFileFromSpec(array $value)
 * @method static array normalizeNestedFileSpec(array $files = [])
 * @method static ServerRequest fromGlobals()
 * @method static array extractHostAndPortFromAuthority($authority)
 * @method static string getUriFromGlobals()
 * @method array getServerParams()
 * @method array getUploadedFiles()
 * @method ServerRequest withUploadedFiles(array $uploadedFiles)
 * @method array getCookieParams()
 * @method ServerRequest withCookieParams(array $cookies)
 * @method array getQueryParams()
 * @method ServerRequest withQueryParams(array $query)
 * @method array getParsedBody()
 * @method ServerRequest withParsedBody($data)
 * @method array getAttributes()
 * @method mixed getAttribute($attribute, $default = null)
 * @method ServerRequest withAttribute($attribute, $value)
 * @method ServerRequest withoutAttribute($attribute)
 *
 * @see \GuzzleHttp\Psr7\ServerRequest
 */
class ServerRequestProxy
{
    use ServerRequestExtend;

    private static $instance;

    /**
     * @var ServerRequest;
     */
    private $serverRequest;

    private function __construct(RequestInterface $serverRequest)
    {
        $this->serverRequest = $serverRequest;
    }

    public static function fromGlobalsAndHandleJson()
    {
        if (!is_null(self::$instance)) {
            return self::$instance;
        }
        $serverRequest = ServerRequest::fromGlobals();

        // 如果传入header头是json格式
        // 获取json数据解析成数组放到post数据中
        $contentType = $serverRequest->getHeader("Content-Type");
        if (!empty($contentType) && false !== strpos($contentType[0], 'application/json')) {
            $json = $serverRequest->getBody()->getContents();
            $array = json_decode($json, true);
            $serverRequest = $serverRequest->withParsedBody($array);
        }

        return self::$instance = new self($serverRequest);
    }

    public function getServerRequest()
    {
        return $this->serverRequest;
    }

    public function getByKey(string $key, $clearCache = false)
    {
        static $parsedBody;
        static $queryParams;
        static $bodyAlready = false;
        static $queryParamsAlready = false;

        if ($clearCache) {
            $bodyAlready = false;
            $queryParamsAlready = false;
        }

        if (!$bodyAlready) {
            $parsedBody = $this->serverRequest->getParsedBody();
            $bodyAlready = true;
        }
        if (!$queryParamsAlready) {
            $queryParams = $this->serverRequest->getQueryParams();
            $queryParamsAlready = true;
        }

        if (isset($parsedBody[$key])) {
            return $parsedBody[$key];
        }

        if (isset($queryParams[$key])) {
            return $queryParams[$key];
        }

        return null;
    }


    public function getIn($key, $default = null)
    {
        if (is_array($key)) {
            return $this->getInArray($key);
        }

        $ret = $this->getByKey($key);

        return is_null($ret) ? $default : $ret;
    }

    public function getInArray(array $keys, $default = null, $fillDefault = true): array
    {
        $data = [];
        foreach ($keys as $key) {
            $ret = $this->getByKey($key);
            if (!is_null($ret)) {
                $data[$key] = $ret;
                continue;
            }

            if ($fillDefault) {
                $data[$key] = $default;
            }
        }

        return $data;
    }

    public static function __callStatic($name, $arguments)
    {
        if (!is_null(self::$instance)) {
            return self::$instance;
        }

        return call_user_func_array([ServerRequest::class, $name], $arguments);
    }

    public function __call($name, $arguments)
    {
        return call_user_func_array([$this->serverRequest, $name], $arguments);
    }

//    public function getProtocolVersion()
//    {
//        return $this->serverRequest->getProtocolVersion();
//    }
//
//    public function withProtocolVersion($version)
//    {
//        $this->serverRequest = $this->serverRequest->withProtocolVersion($version);
//        return $this;
//    }
//
//    public function getHeaders()
//    {
//        return $this->serverRequest->getHeaders();
//    }
//
//    public function hasHeader($name)
//    {
//        return $this->serverRequest->hasHeader($name);
//    }
//
//    public function getHeader($name)
//    {
//        return $this->serverRequest->getHeader($name);
//    }
//
//    public function getHeaderLine($name)
//    {
//        return $this->serverRequest->getHeaderLine($name);
//    }
//
//    public function withHeader($name, $value)
//    {
//        $this->serverRequest = $this->serverRequest->withHeader($name, $value);
//        return $this;
//    }
//
//    public function withAddedHeader($name, $value)
//    {
//        $this->serverRequest = $this->serverRequest->withAddedHeader($name, $value);
//        return $this;
//    }
//
//    public function withoutHeader($name)
//    {
//        $this->serverRequest = $this->serverRequest->withoutHeader($name);
//        return $this;
//    }
//
//    public function getBody()
//    {
//        return $this->serverRequest->getBody();
//    }
//
//    public function withBody(StreamInterface $body)
//    {
//        $this->serverRequest = $this->serverRequest->withBody($body);
//        return $this;
//    }
//
//    public function getRequestTarget()
//    {
//        return $this->serverRequest->getRequestTarget();
//    }
//
//    public function withRequestTarget($requestTarget)
//    {
//        $this->serverRequest = $this->serverRequest->withRequestTarget($requestTarget);
//        return $this;
//    }
//
//    public function withMethod($method)
//    {
//        $this->serverRequest = $this->serverRequest->withMethod($method);
//        return $this;
//    }
//
//    public function getUri()
//    {
//        return $this->serverRequest->getUri();
//    }
//
//    public function withUri(UriInterface $uri, $preserveHost = false)
//    {
//        $this->serverRequest = $this->serverRequest->withUri($uri, $preserveHost);
//        return $this;
//    }
//
//    public function getServerParams()
//    {
//        return $this->serverRequest->getServerParams();
//    }
//
//    public function getCookieParams()
//    {
//        return $this->serverRequest->getCookieParams();
//    }
//
//    public function withCookieParams(array $cookies)
//    {
//        $this->serverRequest = $this->serverRequest->withCookieParams($cookies);
//        return $this;
//    }
//
//    public function getQueryParams()
//    {
//        return $this->serverRequest->getQueryParams();
//    }
//
//    public function withQueryParams(array $query)
//    {
//        $this->serverRequest = $this->serverRequest->withQueryParams($query);
//        return $this;
//    }
//
//    public function getUploadedFiles()
//    {
//        return $this->serverRequest->getUploadedFiles();
//    }
//
//    public function withUploadedFiles(array $uploadedFiles)
//    {
//        $this->serverRequest = $this->serverRequest->withUploadedFiles($uploadedFiles);
//        return $this;
//    }
//
//    public function getParsedBody()
//    {
//        return $this->serverRequest->getParsedBody();
//    }
//
//    public function withParsedBody($data)
//    {
//        $this->serverRequest = $this->serverRequest->withParsedBody($data);
//        return $this;
//    }
//
//    public function getAttributes()
//    {
//        return $this->serverRequest->getAttributes();
//    }
//
//    public function getAttribute($name, $default = null)
//    {
//        return $this->serverRequest->getAttribute($name, $default = null);
//    }
//
//    public function withAttribute($name, $value)
//    {
//        $this->serverRequest = $this->serverRequest->withAttribute($name, $value);
//        return $this;
//    }
//
//    public function withoutAttribute($name)
//    {
//        $this->serverRequest = $this->serverRequest->withoutAttribute($name);
//        return $this;
//    }
}
